// regeneratorRuntime modified for persistency by artheru

import Promise from "promise";

function $RR_hasOwn(what, p) {
    return typeof what[p] !== "undefined";
}

let runtime = {};

function $RR_genGenerator() {
    let ret = {
        next: function $RR_gGNext(arg){return  ret._invoke("next", arg)},
        "throw": function $RR_gGThrow(arg){return  ret._invoke("throw", arg)},
        "return": function $RR_gGReturn(arg){return ret._invoke("return", arg)},
    };
    return ret;
}

function $RR_wrap(innerFn, outerFn, self, tryLocsList) {
    const generator = $RR_genGenerator();
    const context = new $RR_regenContext(tryLocsList || []);
    generator._invoke = $RR_makeInvokeMethod(innerFn, self, context);
    return generator;
}

runtime.wrap = $RR_wrap;

function $RR_tryCatch(fn, obj, arg) {
    try {
        return {type: "normal", arg: fn.call(obj, arg)};
    } catch (err) {
        return {type: "throw", arg: err};
    }
}

const GenStateSuspendedStart = "suspendedStart";
const GenStateSuspendedYield = "suspendedYield";
const GenStateExecuting = "executing";
const GenStateCompleted = "completed";

const ContinueSentinel = {};

runtime.isGeneratorFunction = function $RR_isGeneratorFunction (genFun) {
    return typeof genFun === "function" && typeof genFun.generator !== "undefined";
};

runtime.mark = function $RR_mark (genFun) {
    genFun.generator == true;
    return genFun;
};

runtime.awrap = function $RR_awrap (arg) {
    return {__await: arg};
};

function $RR_AsyncIterator(generator) {
    let self = this;

    function $RR_regeneratorAsyncInvoke(method, arg, resolve, reject) {
        const record = $RR_tryCatch(generator[method], generator, arg);
        if (record.type === "throw") {
            reject(record.arg);
        } else {
            const result = record.arg;
            let value = result.value;
            if (value &&
                typeof value === "object" &&
                $RR_hasOwn(value, "__await")) {
                return Promise.resolve(value.__await).then(function $RR_res1(value) {
                    $RR_regeneratorAsyncInvoke("next", value, resolve, reject);
                }, function $RR_res2(err) {
                    $RR_regeneratorAsyncInvoke("throw", err, resolve, reject);
                });
            }

            return Promise.resolve(value).then(function $RR_res3(unwrapped) {
                result.value = unwrapped;
                resolve(result);
            }, reject);
        }
    }

    let previousPromise;

    function $RR_regeneratorEnqueue(method, arg) {
        function $RR_callInvokeWithMethodAndArg() {
            return new Promise(function $RR_callInvokeWithMethodAndArgF0(resolve, reject) {
                $RR_regeneratorAsyncInvoke(method, arg, resolve, reject);
            });
        }

        return previousPromise =
            previousPromise ? previousPromise.then(
                $RR_callInvokeWithMethodAndArg,
                $RR_callInvokeWithMethodAndArg
            ) : $RR_callInvokeWithMethodAndArg();
    }

    this._invoke = $RR_regeneratorEnqueue;

    this["next"] = function $RR_AINext(arg){return $RR_regeneratorEnqueue("next", arg)};
    this["throw"] = function $RR_AIThrow(arg){return $RR_regeneratorEnqueue("throw", arg)};
    this["return"] = function $RR_AIReturn(arg){return $RR_regeneratorEnqueue("return", arg)};
    this["@@asyncIterator"] = function $RR_AIS() {return self};
}

runtime.async = function $RR_async (innerFn, outerFn, self, tryLocsList) {
    const iter = new $RR_AsyncIterator(
        $RR_wrap(innerFn, outerFn, self, tryLocsList)
    );

    return runtime.isGeneratorFunction(outerFn)
        ? iter // If outerFn is a generator, return the full iterator.
        : iter.next().then(function $RR_aRes(result) {
            return result.done ? result.value : iter.next();
        });
};

function $RR_makeInvokeMethod(innerFn, self, context) {
    let state = GenStateSuspendedStart;

    return function $RR_mIMInvoke(method, arg) {
        if (state === GenStateExecuting) {
            throw new Error("Generator is already running");
        }

        if (state === GenStateCompleted) {
            if (method === "throw") {
                throw arg;
            }
            return $RR_regenDoneResult();
        }

        context.method = method;
        context.arg = arg;

        while (true) {
            const delegate = context.delegate;
            if (delegate) {
                const delegateResult = $RR_maybeInvokeDelegate(delegate, context);
                if (delegateResult) {
                    if (delegateResult === ContinueSentinel) continue;
                    return delegateResult;
                }
            }
            if (context.method === "next") {
                context.sent = context._sent = context.arg;

            } else if (context.method === "throw") {
                if (state === GenStateSuspendedStart) {
                    state = GenStateCompleted;
                    throw context.arg;
                }

                context.dispatchException(context.arg);

            } else if (context.method === "return") {
                context.abrupt("return", context.arg);
            }

            state = GenStateExecuting;

            const record = $RR_tryCatch(innerFn, self, context);
            if (record.type === "normal") {
                state = context.done
                    ? GenStateCompleted
                    : GenStateSuspendedYield;

                if (record.arg === ContinueSentinel) {
                    continue;
                }

                return {
                    value: record.arg,
                    done: context.done
                };

            } else if (record.type === "throw") {
                state = GenStateCompleted;
                // Dispatch the exception by looping back around to the
                // context.dispatchException(context.arg) call above.
                context.method = "throw";
                context.arg = record.arg;
            }
        }
    };
}

function $RR_maybeInvokeDelegate(delegate, context) {
    const method = delegate.iterator[context.method];
    if (method === undefined) {
        context.delegate = null;

        if (context.method === "throw") {
            if (delegate.iterator.return) {
                context.method = "return";
                context.arg = undefined;
                $RR_maybeInvokeDelegate(delegate, context);

                if (context.method === "throw") {
                    return ContinueSentinel;
                }
            }

            context.method = "throw";
            context.arg = new TypeError(
                "The iterator does not provide a 'throw' method");
        }

        return ContinueSentinel;
    }

    const record = $RR_tryCatch(method, delegate.iterator, context.arg);

    if (record.type === "throw") {
        context.method = "throw";
        context.arg = record.arg;
        context.delegate = null;
        return ContinueSentinel;
    }

    let info = record.arg;

    if (!info) {
        context.method = "throw";
        context.arg = new TypeError("iterator result is not an object");
        context.delegate = null;
        return ContinueSentinel;
    }

    if (info.done) {
        // Assign the result of the finished delegate to the temporary
        // variable specified by delegate.resultName (see delegateYield).
        context[delegate.resultName] = info.value;

        // Resume execution at the desired location (see delegateYield).
        context.next = delegate.nextLoc;

        // If context.method was "throw" but the delegate handled the
        // exception, let the outer generator proceed normally. If
        // context.method was "next", forget context.arg since it has been
        // "consumed" by the delegate iterator. If context.method was
        // "return", allow the original .return call to continue in the
        // outer generator.
        if (context.method !== "return") {
            context.method = "next";
            context.arg = undefined;
        }

    } else {
        // Re-yield the result returned by the delegate method.
        return info;
    }

    // The delegate iterator is finished, so forget it and continue with
    // the outer generator.
    context.delegate = null;
    return ContinueSentinel;
}

function $RR_pushTryEntry(locs) {
    const entry = {tryLoc: locs[0]};

    if (1 in locs) {
        entry.catchLoc = locs[1];
    }

    if (2 in locs) {
        entry.finallyLoc = locs[2];
        entry.afterLoc = locs[3];
    }

    this.tryEntries.push(entry);
}

function $RR_resetTryEntry(entry) {
    const record = entry.completion || {};
    record.type = "normal";
    delete record.arg;
    entry.completion = record;
}

function $RR_values(iterable) {
    if (iterable) {
        if (typeof iterable.next === "function") {
            return iterable;
        }
        if (!isNaN(iterable.length)) {
            let i = -1;
            let ret = function $RR_valuesF0() {
                while (++i < iterable.length) {
                    if ($RR_hasOwn(iterable, i)) {
                        ret.value = iterable[i];
                        ret.done = false;
                        return ret;
                    }
                }

                ret.value = undefined;
                ret.done = true;
                return ret;
            };
            ret["next"] = ret;
            //let x=ret;
            return ret;
            //return next.next = next;
        }
    }

    // Return an iterator with no values.
    return {next: $RR_regenDoneResult};
}

function $RR_regenDoneResult() {
    return {value: undefined, done: true};
}

function $RR_regenContext(tryLocsList) {
    let self = this;
    this.reset = function $RR_regeneratorContextReset(skipTempReset) {
        self.prev = 0;
        self.next = 0;
        self.done = false;
        self.delegate = null;
        self.method = "next";
        self.arg = undefined;

        self.tryEntries.forEach($RR_resetTryEntry);

        if (!skipTempReset) {
            for (let name in self) {
                // Not sure about the optimal order of these conditions:
                if (name.charAt(0) === "t" &&
                    $RR_hasOwn(self, name) &&
                    !isNaN(+name.slice(1))) {
                    self[name] = undefined;
                }
            }
        }
    };

    this.stop = function $RR_regeneratorContextStop() {
        self.done = true;

        const rootEntry = self.tryEntries[0];
        const rootRecord = rootEntry.completion;
        if (rootRecord.type === "throw") {
            throw rootRecord.arg;
        }

        return self.rval;
    };

    this.dispatchException = function $RR_regeneratorContextdispatchException(exception) {
        if (self.done) {
            throw exception;
        }

        const context = self;
        let record;

        function $RR_dEHandle(loc, caught) {
            record.type = "throw";
            record.arg = exception;
            context.next = loc;

            if (caught) {
                context.method = "next";
                context.arg = undefined;
            }
            return !!caught;
        }

        for (let i = self.tryEntries.length - 1; i >= 0; --i) {
            const entry = self.tryEntries[i];
            record = entry.completion;

            if (entry.tryLoc === "root")
                return $RR_dEHandle("end");

            if (entry.tryLoc <= self.prev) {
                const hasCatch = $RR_hasOwn(entry, "catchLoc");
                const hasFinally = $RR_hasOwn(entry, "finallyLoc");

                if (hasCatch && hasFinally) {
                    if (self.prev < entry.catchLoc) {
                        return $RR_dEHandle(entry.catchLoc, true);
                    } else if (self.prev < entry.finallyLoc) {
                        return $RR_dEHandle(entry.finallyLoc);
                    }

                } else if (hasCatch) {
                    if (self.prev < entry.catchLoc) {
                        return $RR_dEHandle(entry.catchLoc, true);
                    }

                } else if (hasFinally) {
                    if (self.prev < entry.finallyLoc) {
                        return $RR_dEHandle(entry.finallyLoc);
                    }

                } else {
                    throw new Error("try statement without catch or finally");
                }
            }
        }
    };

    this.abrupt = function $RR_regeneratorContextAbrupt(type, arg) {
        let finallyEntry;
        for (let i = self.tryEntries.length - 1; i >= 0; --i) {
            const entry = self.tryEntries[i];
            if (entry.tryLoc <= self.prev &&
                $RR_hasOwn(entry, "finallyLoc") &&
                self.prev < entry.finallyLoc) {
                finallyEntry = entry;
                break;
            }
        }

        if (finallyEntry &&
            (type === "break" ||
                type === "continue") &&
            finallyEntry.tryLoc <= arg &&
            arg <= finallyEntry.finallyLoc) {
            // Ignore the finally entry if control is not jumping to a
            // location outside the try/catch block.
            finallyEntry = null;
        }

        const record = finallyEntry ? finallyEntry.completion : {};
        record.type = type;
        record.arg = arg;

        if (finallyEntry) {
            self.method = "next";
            self.next = finallyEntry.finallyLoc;
            return ContinueSentinel;
        }

        return self.complete(record);
    };

    this.complete = function $RR_regeneratorContextComplete(record, afterLoc) {
        if (record.type === "throw") {
            throw record.arg;
        }

        if (record.type === "break" ||
            record.type === "continue") {
            self.next = record.arg;
        } else if (record.type === "return") {
            self.rval = self.arg = record.arg;
            self.method = "return";
            self.next = "end";
        } else if (record.type === "normal" && afterLoc) {
            self.next = afterLoc;
        }

        return ContinueSentinel;
    };

    this.finish = function $RR_regeneratorContextFinish(finallyLoc) {
        for (let i = self.tryEntries.length - 1; i >= 0; --i) {
            const entry = self.tryEntries[i];
            if (entry.finallyLoc === finallyLoc) {
                self.complete(entry.completion, entry.afterLoc);
                $RR_resetTryEntry(entry);
                return ContinueSentinel;
            }
        }
    };

    this["catch"] = function $RR_regeneratorContextCatch(tryLoc) {
        for (let i = self.tryEntries.length - 1; i >= 0; --i) {
            const entry = self.tryEntries[i];
            if (entry.tryLoc === tryLoc) {
                const record = entry.completion;
                if (record.type === "throw") {
                    let thrown = record.arg;
                    $RR_resetTryEntry(entry);
                }
                return thrown;
            }
        }
        throw new Error("illegal catch attempt");
    };

    this.delegateYield = function $RR_regeneratorContextDelegateYield(iterable, resultName, nextLoc) {

        self.delegate = {
            iterator: $RR_values(iterable),
            resultName: resultName,
            nextLoc: nextLoc
        };

        if (self.method === "next")
            self.arg = undefined;

        return ContinueSentinel;
    };
    this.tryEntries = [{tryLoc: "root"}];
    tryLocsList.forEach($RR_pushTryEntry, this);
    this.reset(true);
}

export default runtime;